bfdd, lib: extend ZEBRA_BFD_DEST_REGISTER with SBFD/SRv6 fields#22068
bfdd, lib: extend ZEBRA_BFD_DEST_REGISTER with SBFD/SRv6 fields#22068rajshekhar-nexthop wants to merge 2 commits into
Conversation
b393cb7 to
ba45a6b
Compare
|
|
||
| /* | ||
| * SBFD over SRv6 fields carried in the optional tail of | ||
| * the ZEBRA_BFD_DEST_REGISTER payload. Defaults of zero match |
There was a problem hiding this comment.
what's the relationship with the BFD_DEST_UPDATE message, which has a handler in lib/bfd.c ?
There was a problem hiding this comment.
Thanks for the review, and apologies for the delay in getting back.
This is ZEBRA_INTERFACE_BFD_DEST_UPDATE — the bfdd → client state-notification path (handled in lib/bfd.c by zclient_bfd_session_update → bfd_get_peer_info). This PR doesn't touch it; only the client → bfdd register/update/deregister side is extended.
The notification already carries bfd_name (written in ptm_bfd_notify, parsed by bfd_get_peer_info), and SBFD sessions are keyed on bfd_name in gen_bfd_key, so clients can match the notification back to their session without any change there.
If you feel SBFD-aware fields should also ride on the notification, happy to take that up as a follow-up.
| * - c: bfd_name length | ||
| * - X bytes: bfd_name | ||
| * | ||
| * The encoder emits the tail only when at least one SBFD field is |
There was a problem hiding this comment.
yes, I agree with the comment about the fragility of this design. it would be better to have explicit indications about the additional optional fields, as we do with the route zapi messages.
There was a problem hiding this comment.
Thanks for the review and the pointer to the route-zapi pattern, and sorry for the delay in turning this around.
Agreed, reworked in v2 (force-pushed). Optional tail now starts with a uint16 bfd_regext_flags word and per-field BFD_REGEXT_FLAG_* bits, modeled on ZAPI_MESSAGE_* in lib/zclient.h. remote_discr == 0 for sbfd_init is now unambiguous, bfd_name is no longer coupled to SBFD mode, and new fields can just claim a new bit.
While at it, also fixed three issues the old gate-by-value design had:
DEST_DEREGISTERnow carries the tail too — withoutbfd_name,bs_peer_findcouldn't locate named sessions for deletion._bfd_session_updatepropagatesout_sip6/seg_list/remote_discron re-register (gated by flag bits) — so pathd can change a session's SR path without bouncing it.- Decoder rejects
SBFD_INITmode withoutFLAG_REMOTE_DISCR(RFC 7881 §3, discriminator 0 reserved).
Please have a look when you get a chance.
ba45a6b to
850a93d
Compare
850a93d to
c2cabaa
Compare
Append an optional flag-gated tail to the ZEBRA_BFD_DEST_REGISTER / DEST_UPDATE / DEST_DEREGISTER ZAPI payload so external BFD clients (e.g. pathd) can register S-BFD sessions over ZAPI without introducing a new opcode. Modeled on the ZAPI_MESSAGE_* pattern in lib/zclient.h: a leading uint16 flags word in the optional tail indicates which optional fields follow, so "field absent" and "field set to zero" remain distinguishable (remote_discr == 0 on sbfd_init is a legitimate value) and new optional fields can be added by claiming a new bit without reshuffling positional probing. The wire-format extension is backward compatible: * lib/bfd.c::zclient_bfd_command emits the flags word + flagged fields iff `bfd_regext_flags != 0`. Includes deregister frames so a named session (whose key includes `bfd_name`) can be located for deletion; classical-BFD callers leave `bfd_regext_flags` zero and produce byte-identical wire frames. Rejects nonsensical flag combinations (FLAG_REMOTE_DISCR without FLAG_BFD_MODE, FLAG_BFD_MODE with mode 0) at the encoder boundary. * bfdd/ptm_adapter.c::_ptm_msg_read detects the no-tail case via STREAM_READABLE; on a non-empty tail, reads the flags word into `bpc->bfd_regext_flags` and conditionally reads each flagged field. Bounds-checks `seg_num` and `bfd_name_len` on tail-present frames. * ptm_bfd_sess_new propagates the new fields onto the freshly- allocated bfd_session before bs_registrate (because bs_registrate -> bfd_session_enable dispatches on bs->bfd_mode and bs_peer_find keys on bfd_name). Gating is on `bpc->bfd_regext_flags` bits so an explicit `remote_discr == 0` is honoured. * _bfd_session_update propagates `out_sip6`, `seg_list[]`, and `remote_discr` on re-register, also gated by flag bits. `bfd_mode` and `bfd_name` are deliberately not mutated on update (changing either requires socket re-open / key re-hash, which only the initial-create path performs). Only the S-BFD echo path is exercised end-to-end. The wire layout reserves room for SBFD_INIT but the init-session path is not driven by this change.
Verifies the wire-payload extension landed in the previous patch. The test process spawns a Python ZAPI client inside r1's network namespace, performs the ZEBRA_HELLO / ZEBRA_BFD_CLIENT_REGISTER / ZEBRA_BFD_DEST_REGISTER handshake against zebra, and asserts that bfdd's session table reflects what was sent. Three cases: * test_zapi_sbfd_register_creates_session - register frame carries the full SBFD tail (bfd_regext_flags + bfd_mode + remote_discr + srv6_source_ipv6 + seg_list[2] + bfd_name="zapi-sbfd-test"). Asserts a bfdd peer with the expected destination materialises and that bfd_name was preserved through the tail. * test_zapi_classical_register_still_accepted - register frame omits the SBFD tail entirely (no flags word), exercising both the encoder gate (lib/bfd.c emits no tail when bfd_regext_flags == 0) and the decoder's STREAM_READABLE-gated tail handling. * test_zapi_sbfd_register_deduplicates_by_bfd_name - repeats the SBFD register from the first test and asserts the peer count for the same destination does not grow. This catches a key-table asymmetry (bs_peer_find uses bpc->bfd_name in the lookup key, so ptm_bfd_sess_new must populate bs->key.bfdname on insert - see the production change in the previous patch).
c2cabaa to
38c67d5
Compare
Greptile SummaryThis PR extends the
Confidence Score: 3/5The new-session creation path is correctly ordered and bounds-checked, but a live SBFD session receiving a DEST_UPDATE with new seg_list or out_sip6 will have those fields silently discarded, which could cause incorrect forwarding while BFD still reports Up. The encoder/decoder and new-session path look well-constructed. In bfdd_dest_register the bs != NULL branch only applies the BFD profile and never calls bfd_session_update, so the SBFD update code added to _bfd_session_update is unreachable from the normal ZAPI re-register path. The PR description advertises live path updates as a supported operation but the implementation does not wire this up. bfdd/ptm_adapter.c — the bs != NULL branch in bfdd_dest_register and the comment block at lines 611-625 need reconciliation with the intent to support live SBFD path updates. Important Files Changed
Sequence DiagramsequenceDiagram
participant C as ZAPI Client
participant Z as zebra
participant B as bfdd
C->>Z: ZEBRA_BFD_DEST_REGISTER fixed fields + optional SBFD tail
Z->>B: forward message
B->>B: _ptm_msg_read decode fixed fields
B->>B: "if STREAM_READABLE>0 read flags_w then flagged fields"
B->>B: bfdd_dest_register bs_peer_find
alt session not found
B->>B: ptm_bfd_sess_new set bfd_mode name sids before bs_registrate
B->>B: bs_registrate opens SRH or UDP socket
B->>B: _bfd_session_update timers and profile
else session found
B->>B: bfd_profile_apply only SBFD fields NOT updated
end
B->>C: ZEBRA_INTERFACE_BFD_DEST_UPDATE state notification
|
Summary
Append an optional flag-gated tail to the
ZEBRA_BFD_DEST_REGISTER/DEST_UPDATE/DEST_DEREGISTERZAPI payload so external BFD clients(e.g.
pathd) can register S-BFD over SRv6 sessions withoutintroducing a new opcode.
Modeled on the
ZAPI_MESSAGE_*pattern inlib/zclient.h: a leadinguint16flags word indicates which optional fields follow, so"field absent" and "field set to zero" remain distinguishable
(
remote_discr == 0forsbfd_initis a legitimate value), and newoptional fields can be added by claiming a new bit without reshuffling
positional probing on either side.
Tail format:
Wire compatibility: classical-BFD callers leave
bfd_regext_flagszero and produce byte-identical wire frames to the pre-extension
layout. The decoder detects the no-tail case via
STREAM_READABLE;on a non-empty tail, it reads the flags word and conditionally reads
each flagged field. Bytes past the last flagged field this decoder
recognises are silently ignored, so newer senders may append fields
by claiming new flag bits.
Implementation notes:
lib/bfd.c::zclient_bfd_commandemits the flags word + flaggedfields iff
bfd_regext_flags != 0— including on deregister frames,because
bfd_nameparticipates ingen_bfd_keyand a named sessioncannot be located for deletion without it. Encoder also rejects
FLAG_REMOTE_DISCRwithoutFLAG_BFD_MODE, andFLAG_BFD_MODEwithclassical mode (0).
bfdd/ptm_adapter.c::_ptm_msg_readpopulatesbpc->bfd_regext_flagsand conditionally reads each flagged field.Bounds-checks
seg_num,bfd_name_len, andbfd_mode. RejectsSBFD_INITmode withoutFLAG_REMOTE_DISCR(RFC 7881 §3,discriminator 0 reserved).
ptm_bfd_sess_newpropagates the new fields onto the freshlyallocated
bfd_sessionbeforebs_registrate, sincebs_registrate -> bfd_session_enabledispatches onbs->bfd_modeto pick the SRH vs classical UDP socket, and
bs_peer_findkeys onbfd_name. Gating is onbpc->bfd_regext_flagsbits._bfd_session_updatepropagatesout_sip6,seg_list[], andremote_discron re-register, also gated by flag bits — so a clientcan change a session's SRv6 path without tearing it down.
bfd_modeand
bfd_nameare deliberately not mutated on update (would requiresocket re-open / key re-hash; callers wanting that must deregister
Only the S-BFD echo path is exercised end-to-end. The wire layout
reserves room for
SBFD_INITbut the init-session path is not drivenby this change.
Commits:
bfdd, lib: extend ZEBRA_BFD_DEST_REGISTER with SBFD/SRv6 fieldstests: topotest for ZEBRA_BFD_DEST_REGISTER SBFD tail— threecases: SBFD register, classical (no-tail) register, and same-name
SBFD re-register dedupe.
Related Issue
N/A
Components
bfdd, lib, tests